home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 1 / Macwelt DVD 1.toast / Web-Publishing / HTML-Editoren / Alpha ƒ / Tcl / Modes / cMode.tcl < prev    next >
Encoding:
Text File  |  2000-12-18  |  26.2 KB  |  806 lines

  1. ## -*-Tcl-*- (install) (nowrap)
  2.  # ###################################################################
  3.  #    Vince's    Additions -    an extension package for Alpha
  4.  # 
  5.  #    FILE: "cMode.tcl"
  6.  #                    created: 19/4/96 {4:53:38 pm}    
  7.  #                 last update: 12/18/2000 {11:01:55 AM}    
  8.  #    Author:    Vince Darley
  9.  #    E-mail:    <vince@santafe.edu>
  10.  #      mail:    317 Paseo de Peralta, Santa Fe, NM 87501, USA
  11.  #       www:    <http://www.santafe.edu/~vince/>
  12.  #    
  13.  # 
  14.  #  modified   by  rev reason
  15.  #  ---------- --- --- -----------
  16.  #  2000-12-07 DWH 1.0 added help text
  17.  # ###################################################################
  18.  ##
  19.  
  20. alpha::mode C 1.3.0 dummyC { *.r } \
  21.   {electricReturn electricBraces electricSemicolon} {} help {
  22.     C and C++ modes function nearly indentically in Alpha.  C Mode
  23.     provides keyword coloring and procedure marking with the Marks Menu;
  24.     supports cmd-dbl-click for opening files or (sends the word to app
  25.     sig "DanR"); automatically handles indentation and formatting. 
  26.     Support for Think and CodeWarrior.  
  27.  
  28.     Click on this "C++ Example.c" link for an example syntax file.
  29. }
  30.  
  31. alpha::mode C++ 1.3.0 dummyC++ \
  32.   {*.H *.c *.h *.cc *.cp *.cpp *.CPP *.C *.pch *.pch++ *.icc *.exp *.c++} \
  33.   {electricReturn electricBraces electricSemicolon} {} help {
  34.     C and C++ modes function nearly indentically in Alpha.  C Mode
  35.     provides keyword coloring and procedure marking with the Marks Menu;
  36.     supports cmd-dbl-click for opening files or (sends the word to app
  37.     sig "DanR"); automatically handles indentation and formatting. 
  38.     Support for Think and CodeWarrior.  
  39.  
  40.     Click on this "C++ Example.c" link for an example syntax file.
  41. }
  42.  
  43. # Not really required any more.
  44. # The CW menu registers a proc to call cw IDE.
  45. # hook::register saveHook modified "C" "C++"
  46.  
  47. proc dummyC {} {}
  48. proc dummyC++ {} {}
  49.  
  50. newPref f elecColon {1} C
  51. newPref v leftFillColumn {3} C
  52. newPref v prefixString {//} C
  53. newPref f elecElse {1} C
  54. newPref f wordWrap {0} C
  55. newPref v funcExpr {^[^ \t\(#\r/@].*\(.*\)$} C
  56. if {[info tclversion] < 8.0} {
  57.     newPref v parseExpr {\b(([_\w]+::~?)*[_\w]+)\s*\(} C
  58. } else {
  59.     newPref v parseExpr {\m(([_\w]+::~?)*[_\w]+)\s*\(} C
  60. }
  61. newPref v wordBreak {[_\w]+} C
  62. newPref v wordBreakPreface {[^_\w]} C
  63. newPref f autoMark 0 C
  64. newPref color stringColor green C
  65. newPref color commentColor red C
  66. newPref color funcColor yellow C
  67. newPref color keywordColor blue C
  68. newPref sig CWCompSig CWIE C
  69. newPref sig CWDbgSig MWDB C
  70. newPref sig SymCompSig KAHL C
  71. newPref sig SymDbgSig {◊LSD} C
  72. newPref f includeMenu {1} C
  73. newPref f launchIDEifRequired {1} C
  74. newPref v sourceSuffices { .c } C
  75. newPref v headerSuffices { .h } C
  76. newPref v indentComments "code 0" C
  77. newPref v indentMacros "fixed 0" C
  78. newPref v IDE 0 C "" [list "CodeWarrior" "Symantec" "none"] index
  79. newPref f useFasterButWorseIndentation 0 C
  80. set C::commentRegexp    {/\*(([^*]/)|[^*]|\r)*\*/}
  81. set cPreRegexp        {^\#[\t ]*[a-z]*}
  82. set cKeyWords {
  83.     void break register short enum extern int for if while struct static 
  84.     long continue switch case char unsigned double float return else 
  85.     default goto do pascal Boolean typedef volatile union auto sizeof 
  86.     size_t
  87. }
  88. if {[info exists Cwords]} {set cKeyWords [concat $cKeyWords $Cwords]}
  89. regModeKeywords -e {//} -b {/*} {*/} -c $CmodeVars(commentColor) \
  90.   -f $CmodeVars(funcColor) -k $CmodeVars(keywordColor) \
  91.   -s $CmodeVars(stringColor) -m {#} C $cKeyWords
  92.  
  93. #================================================================================
  94.  
  95. newPref f elecColon {1} C++
  96. newPref v leftFillColumn {3} C++
  97. newPref v prefixString {//} C++
  98. newPref v wordBreak {[\w_]+} C++
  99. newPref v wordBreakPreface {[^_\w]} C++
  100. newPref f elecElse {1} C++
  101. newPref f wordWrap {0} C++
  102. newPref v funcExpr {^([^ \t\(#[\r\n]/@].*[ \t]+)?\*?([A-Za-z0-9~_]+(<[^>]*>)?::[-A-Za-z0-9~_+= <>\|\*/]+|[A-Za-z0-9~_]+)[ \t\r\n]*\(} C++
  103. if {[info tclversion] < 8.0} {
  104.     newPref v parseExpr {\b(([_\w]+::~?)*[_\w]+)\s*\(} C++
  105. } else {
  106.     newPref v parseExpr {\m(([_\w]+::~?)*[_\w]+)\s*\(} C++
  107. }
  108. newPref f autoMark 0 C++
  109. newPref color stringColor green C++
  110. newPref color commentColor red C++
  111. newPref color keywordColor blue C++
  112. newPref color funcColor yellow C++
  113. newPref sig CWCompSig CWIE C++
  114. newPref sig CWDbgSig MWDB C++
  115. newPref sig SymCompSig KAHL C++
  116. newPref sig SymDbgSig {◊LSD} C++
  117. newPref f includeMenu {1} C++
  118. newPref f launchIDEifRequired {1} C++
  119. newPref v sourceSuffices { .cc .cp .cpp .c .icc .C } C++
  120. newPref v headerSuffices { .h .hh } C++
  121. # These three are pairs:
  122. newPref v indentComments "code 0" C++ "" indentationTypes varitem
  123. newPref v indentC++Comments "code 0" C++ "" indentationTypes varitem
  124. newPref v indentMacros "fixed 0" C++ "" indentationTypes varitem
  125. newPref v IDE 0 C++ "" [list "CodeWarrior" "Symantec" "none"] index
  126. newPref f useFasterButWorseIndentation 0 C++
  127. newPref folder universalHeadersFolder "" C++
  128.  
  129. proc C++::openUniversalHeader {} {
  130.     global universalHeadersFolder tabSize
  131.     if {![file exists $universalHeadersFolder]} {
  132.     alertnote "Please set your 'Universal Headers Folder' preference first.\
  133.       It's set to '$universalHeadersFolder' which doesn't exist."
  134.     return
  135.     }
  136.     set filename [prompt::statusLineComplete "Open which header" \
  137.       [list file::completeFromDir $universalHeadersFolder] -nocache \
  138.       -tryuppercase]
  139.     set old $tabSize
  140.     set tabSize 4
  141.     file::openQuietly [file join $universalHeadersFolder $filename]
  142.     set tabSize $old
  143. }
  144.  
  145. Bind 'q' <o> C++::openUniversalHeader C++
  146.  
  147. set C++::commentRegexp    {/\*(([^*]/)|[^*]|\r)*\*/}
  148.  
  149. set {c++KeyWords} {
  150.     new delete explicit class friend protected private public template try 
  151.     catch throw operator const mutable virtual asm inline this and and_eq 
  152.     bitand bitor compl not or or_eq xor xor_eq not_eq wchar_t bool true 
  153.     false bool inline mutable static_cast dynamic_cast reinterpret_cast 
  154.     typeid using namespace inherited
  155. }
  156. if {[info exists {C++words}]} {
  157.     set {c++KeyWords} [concat ${c++KeyWords} ${C++words} $cKeyWords]
  158. } else {
  159.     set {c++KeyWords} [concat ${c++KeyWords} $cKeyWords]
  160. }
  161.  
  162. regModeKeywords -e {//} -b {/*} {*/} -c [set C++modeVars(commentColor)] \
  163.   -f [set C++modeVars(funcColor)] -k [set C++modeVars(keywordColor)] \
  164.   -s [set C++modeVars(stringColor)] -m {#} {C++} ${c++KeyWords}
  165. unset cKeyWords
  166. unset {c++KeyWords}
  167.  
  168. proc C++::DblClick {from to shift option control} {    
  169.     if {[regexp {#include.*("|<)(.*)("|>)} [getText \
  170.       [lineStart [getPos]] [nextLineStart [getPos]]] "" "" inc]} {
  171.     return [file::tryAndOpen $inc]
  172.     }
  173.     
  174.     select $from $to
  175.     set text [getSelect]
  176.     
  177.     global tagFile
  178.     set lines [grep "^$text'" $tagFile]
  179.     if {[regexp {'(.*)'(.*[^\t])(\t)+∞} $lines "" one two]} {
  180.     file::openQuietly $one
  181.     set inds [search -s -f 1 -r 0 "$two" [minPos]]
  182.     display [lindex $inds 0]
  183.     eval select $inds
  184.     } else {
  185.     app::launchFore DanR
  186.     AEBuild {'DanR'} DanR {REF } "----" "“$text”"
  187.     }
  188. }
  189.  
  190. proc C++::parseFuncs {} {
  191.     global mode sortFuncsMenu funcExpr parseExpr
  192.     
  193.     set pos [minPos]
  194.     set m {}
  195.     while {[set res [search -s -f 1 -r 1 -i 0 -n $funcExpr $pos]] != ""} {
  196.     if {[regexp -- $parseExpr [eval getText $res] "" word]} {
  197.         lappend m [list $word [lindex $res 0]]
  198.     }
  199.     set pos [lindex $res 1]
  200.     }
  201.     if {$sortFuncsMenu} {
  202.     set m [lsort -ignore $m]
  203.     }
  204.     set files ""
  205.     foreach f [C++::OptionTitlebar] {
  206.     lappend files $f -1
  207.     }
  208.     lappend files "\(-" 0
  209.     foreach pair $m {
  210.     eval lappend files $pair
  211.     }
  212.     return $files
  213. }
  214.  
  215. # for C mode
  216.  
  217. proc C::DblClick {args} { eval C++::DblClick $args }
  218.  
  219. proc C::parseFuncs {} {
  220.     return [C++::parseFuncs]
  221. }
  222.  
  223.  
  224. #############################################################################
  225. #                                        #
  226. # Stuff above this point has only minor modifications from the original        #
  227. # "cMode.tcl", stuff below is largely or totally new.                #
  228. #                                        #
  229. #############################################################################
  230.  
  231. # ◊◊◊◊ File marking ◊◊◊◊ #
  232.  
  233. ## 
  234.  # -------------------------------------------------------------------------
  235.  #     
  236.  # "C++::MarkFile" --
  237.  #    
  238.  #    Improved version which handles templates, operators    etc.
  239.  #    Makes use of the new mark menu in Alpha    6.5 which can handle
  240.  #    more weird characters.  Handles most 'operator =+-*...' functions
  241.  #  for C++
  242.  #  
  243.  #  Better marking of templates recently added.
  244.  # -------------------------------------------------------------------------
  245.  ##
  246. proc C++::MarkFile {} {
  247.     global C++modeVars
  248.     set ext [file extension [win::Current]]
  249.     if { $ext == ".exp"} { return }
  250.     # do we have a header file (1) or a source file (0) :
  251.     set header [expr {[lsearch -exact [set C++modeVars(headerSuffices)] $ext]==-1 ? 0 : 1}]
  252.     if {$header} {
  253.     # Marking the 'struct' declarations
  254.     C++::otherMarks struct
  255.     # Marking the 'class' declarations
  256.     C++::otherMarks class
  257.     }
  258.     # Now mark function definitions and prototypes
  259.     set pos [minPos]
  260.     set markExpr "^(\[A-Za-z0-9~_\]+\[ \t\n\r\]*\\(|(\[^ \t\(#\n\r/@\*\]\[^\r\n\]*\[ \t\]+\\*?)?"
  261.     append markExpr "(\[A-Za-z0-9~_\]+(<\[^>\]*>)?(::)?\[-A-Za-z0-9~_+= <>\|\\*/\]+|\[A-Za-z0-9~_\]+)"
  262.     append markExpr "\[ \n\t\r\]*\\()"
  263.     
  264.     set subMarkExpr "(\[A-Za-z0-9~_\]+(<\[^>\]*>)?(::)?\[-A-Za-z0-9~_+= <>\|\*/\]+|\[A-Za-z0-9~_\]+)\[ \t\]*\\("
  265.     while {![catch {search -s -f 1 -r 1 -m 0 -i 0 "$markExpr" $pos} res]} {
  266.     set pos [lindex $res 1]
  267.     # We do not want to mark typedefs :
  268.     if {[regexp "^typedef" [getText [lindex $res 0] $pos]]} {continue} 
  269.     # if we have a source file, we don't want to mark prototypes :
  270.     if {!$header} {
  271.         # Looking for a semi-colon after the matching closing parenthese
  272.         # with possibly space chars inbetween. 'catch' it in case 'matchIt' fails.
  273.         if {![catch {set loco [pos::math [matchIt "\(" [lindex $res 1]] +1]}]} {
  274.         while {[is::Whitespace [lookAt $loco]]} {
  275.             set loco [pos::math $loco +1]
  276.         }
  277.         if {[lookAt $loco]==";" } {
  278.             continue
  279.         }
  280.         }
  281.     } 
  282.     if {[catch {search -s -f 0 -r 1 -m 0 -l [lindex $res 0] -i 0 \
  283.       {[ \t*][a-zA-Z]} [set pos [pos::math [lindex $res 1] -1]]} start]} {
  284.         set start [lindex $res 0]
  285.         if {[regexp "^\[A-Za-z0-9~_\]+\[ \t\r\n\]*\\(" \
  286.           [getText $start [nextLineStart $start]] thistext]} {
  287.         } else {
  288.         continue
  289.         }
  290.         
  291.     } else {
  292.         set start [lindex $start 0]
  293.         set thistext [getText $start [lindex $res 1]]
  294.     }
  295.     # regexp doesn't like carriage returns or tabs
  296.     # if the open paren was the last character on the line the selected text 
  297.     # included the last carriage return as well
  298.     # trim this off now that it is changed into a space
  299.     regsub -all "\[\r\n\t\]" [string trimright $thistext] " " thistext
  300.     if {[regexp -- $subMarkExpr $thistext dummy word]} {
  301.         if { [string first "::" $word] != -1 } {
  302.         regsub {(<\w+>)?::} $word " " it
  303.         set l [lindex $it 0]
  304.         if { $l == [lindex $it 1] } {
  305.             set word "Construct '$l'"
  306.         } elseif { "~$l" == [lindex $it 1] } {
  307.             set word "Destruct '$l'"
  308.         }
  309.         }
  310.         set inds($word) [lineStart [pos::math $start - 1]]
  311.     }
  312.     }
  313.     if {[info exists inds]} {
  314.     if {$header} {
  315.         setNamedMark "PROTOTYPES:" [minPos] [minPos] [minPos]
  316.     } else {
  317.         setNamedMark "FUNCTIONS:" [minPos] [minPos] [minPos]
  318.     }
  319.     foreach f [lsort -ignore [array names inds]] {
  320.         set next [lineStart $inds($f)]
  321.         regsub "_ANSI_ARGS_" $f "" it
  322.         if {[string length $it] > 57} { set it "[string range $it 0 53]..." }
  323.         setNamedMark "${it}" "$inds($f)" $next $next
  324.     }
  325.     }
  326. }
  327.  
  328. proc C++::otherMarks {type} {
  329.     set pos [minPos]
  330.     set markExpr "($type\[ \t\n\r\]*\[A-Za-z0-9~_\]+"
  331.     append markExpr "\[ \t\n\r\]*(\[A-Za-z0-9~_<>\]+)?\[ \t\n\r\]*\{)"
  332.     while {![catch {search -s -f 1 -r 1 -m 0 -i 0 "$markExpr" $pos} res]} {
  333.     set pos [lindex $res 1]
  334.     set start [lindex $res 0]
  335.     set thistext [getText $start $pos]
  336.     regsub -all "\[\r\n\t\]" [string trimright $thistext] " " thistext
  337.     if {[regexp "$type\[ \t\n\r\]+(\[A-Za-z0-9~_\]+)" $thistext dummy word]} {
  338.         set inds($word) [lineStart [pos::math $start - 1]]
  339.     }
  340.     }
  341.     if {[info exists inds]} {
  342.     setNamedMark "[string toupper $type]:" [minPos] [minPos] [minPos]
  343.     foreach f [lsort -ignore [array names inds]] {
  344.         set next [lineStart $inds($f)]
  345.         set it $f
  346.         if {[string length $it] > 57} { set it "[string range $it 0 53]..." }
  347.         setNamedMark "  ${it}" "$inds($f)" $next $next
  348.     }
  349.     # This is a trick to have a separator line after _each_ category (only the dash is
  350.     # marked, not $type; thus we have different marks producing a separator line) :
  351.     setNamedMark "-$type" [minPos] [minPos] [minPos]
  352.     }
  353. }
  354.  
  355. proc C::MarkFile {} { C++::MarkFile }
  356.  
  357.  
  358. # ◊◊◊◊ Indentation routines ◊◊◊◊ #
  359.  
  360. proc C::indentLine {} {C++::indentLine}
  361.  
  362.  ## 
  363.   # -------------------------------------------------------------------------
  364.   #     
  365.   #    "C++indentLine" --
  366.   #    
  367.   #  More sophisticated version of Pete's.  Handles things like '(...)'
  368.   #  expressions split over multiple lines, if/elseif/else both with
  369.   #  and without curly braces, multiple line stream manipulation with
  370.   #  '<<' or '>>', C and C++ style comments, ...  Assumes indentation
  371.   #  is '4' but any tab-size may be used.
  372.   #     
  373.   #  Current bugs: multi-line ',' separated lists are poorly indented.
  374.   #
  375.   #  Problems:
  376.   #   matchIt's limit doesn't seem to work, so if there is no match and we're
  377.   #   in a large file, we wait up to seconds sometimes.  Alpha bug.
  378.   #           
  379.   #  Currently checking whether we're in a /*...*/ comment is quite
  380.   #  time consuming.  It would be nice if Alpha supplied a hook to do
  381.   #  this for us.
  382.   #   
  383.   # Results: 
  384.   #  Indents the current line correctly ;-) for C, C++ coding
  385.   #     
  386.   # --Version--Author------------------Changes-------------------------------  
  387.   #      1.0      Pete Keleher              original
  388.   #    2.0     <vince@santafe.edu> updated as described above.
  389.   #    2.1     <vince@santafe.edu> faster, better, uses positions not strings
  390.   #    2.2     <vince@santafe.edu> uses 'correctIndentation' sub proc
  391.   # -------------------------------------------------------------------------
  392.   ##
  393. proc C++::indentLine {} {
  394.     # preliminaries
  395.     set beg [lineStart [set p [getPos]]]
  396.     # are we in a C comment /*...*/ if so indent specially and return
  397.     # we really need to work out how to put this in 'correctIndentation'
  398.     if {![catch {C_inCComment $beg} comment]} {
  399.     set fChar [search -s -f 1 -r 1 "\[^ \t\r\n\]" $beg]
  400.     if { [lookAt [lindex $fChar 0]] == "*" } {
  401.         return [eval C_indentCommentLine [list $beg] $comment]
  402.     }
  403.     }
  404.     set text [getText $beg [nextLineStart $beg]]
  405.     if {[regexp "^\[ \t\]*" $text white]} {
  406.     set len [string length $white]
  407.     set rest [string range $text $len end]
  408.     } else {
  409.     set white ""
  410.     set rest ""
  411.     set len 0
  412.     }
  413.     # get indentation level    
  414.     set lwhite [text::indentOf [C++::correctIndentation [getPos] [string trim $rest]]]
  415.     if {$white != $lwhite} {
  416.     replaceText $beg [pos::math $beg + $len] $lwhite
  417.     }
  418.     goto [pos::math $beg + [string length $lwhite]] 
  419.     # To keep relative position.
  420.     #goto [pos::math $p + [string length $lwhite] - $len]
  421. }
  422. proc C::correctIndentation {args} {eval C++::correctIndentation $args}
  423. ## 
  424.  # -------------------------------------------------------------------------
  425.  # 
  426.  # "C++::correctIndentation" --
  427.  # 
  428.  #  Known bugs:
  429.  #  
  430.  #  Lines which contain a URL with :// embedded tend to be considered
  431.  #  a ':' followed by a comment, and are indented as if they were
  432.  #  part of a 'case://comment' statement which is wrong.
  433.  # -------------------------------------------------------------------------
  434.  ##
  435. proc C++::correctIndentation {pos {nextword ""}} {
  436.     # preliminaries
  437.     set beg [lineStart $pos]
  438.     set nextCh [string range $nextword 0 3]
  439.     set nextC [string index $nextCh 0]
  440.     set nextP [string range $nextCh 0 1]
  441.     # check for forced indentation of C, C++ comments and '#' macros
  442.     set ind "code 0"
  443.     switch -- $nextC {
  444.     "\#" {
  445.         global indentMacros
  446.         set ignore_trailers ""
  447.         set ind $indentMacros
  448.     }
  449.     "/" {
  450.         global indentComments indentC++Comments
  451.         set ignore_trailers ""
  452.         if {$nextP == "/*"} {set ind $indentComments}
  453.         if {$nextP == "//"} {set ind ${indentC++Comments}}
  454.     }
  455.     }
  456.     if {[lindex $ind 0] == "fixed" } {
  457.     # force indentation to given level
  458.     return [lindex $ind 1]
  459.     }
  460.     
  461.     # (1) first we get the indent of the last line:
  462.     # this may involve looking back a fair way
  463.     set lst [C_prevCodeIndent [pos::math $beg - 1]]
  464.     
  465.     if {[pos::compare [set pstart [lindex $lst 0]] == [minPos]]} {
  466.     return 0
  467.     }
  468.     set lwhite [posX [pos::math [lindex $lst 1] - 1]]    
  469.     # have we just finished an if-elseif-else with no '{}'?
  470.     if {$nextCh == "else"} {set iselse 1} else { set iselse 0}
  471.     if {![C_isLineNBI $pstart]} {
  472.     incr lwhite [C_recurseNoBraceIndent $pstart 0 $iselse]
  473.     }
  474.     if { [set multi [C_isLineMulti $pstart]] != "-1" } {
  475.     set lwhite $multi
  476.     }
  477.     
  478.     # (2) now we indent this line accordingly
  479.     
  480.     set pbeg [prevLineStart $beg]
  481.     set backpos [nextLineStart [lindex $lst 0]]
  482.     # is there a comment at the end of the line? if so scan back to the character we want
  483.     if {![catch {search -s -f 0 -r 1 -l $pbeg "//\[^\r\n\]*\[\n\r\]" $backpos} compos]} {
  484.     set compos [lindex $compos 0]
  485.     if {[pos::compare $compos > $pbeg]} {
  486.         set backpos [pos::math $compos + 1]
  487.     }    
  488.     }
  489.     global indentationAmount
  490.     if {[pos::compare [set backpos [pos::math $backpos - 2]] > [minPos]]} {
  491.     set lst [search -s -f 0 -r 1 -m 0 "\[^ \t\r\n\]" $backpos]
  492.     switch -- [lookAt [lindex $lst 0]] {
  493.         "\{" {
  494.         incr lwhite $indentationAmount
  495.         } 
  496.         ":" {
  497.         # expression is better for odd indentationAmounts
  498.         incr lwhite [expr {$indentationAmount - $indentationAmount/2}]
  499.         } 
  500.         "\)" {
  501.         # see if we're in a if-elseif-else with no '{}' and indent
  502.         if {[C_isLineNBI $pstart]} {
  503.             incr lwhite $indentationAmount
  504.         }
  505.         }
  506.         "e" {
  507.         if { [getText [pos::math [lindex $lst 0] - 3] \
  508.           [pos::math [lindex $lst 0] + 1]] == "else" } {
  509.             if {[C_isLineNBI $pstart]} {
  510.             incr lwhite $indentationAmount
  511.             }
  512.         }
  513.         
  514.         }
  515.     }
  516.     }
  517.     
  518.     switch -- $nextC {
  519.     "\}" {
  520.         incr lwhite [expr -$indentationAmount]
  521.     }
  522.     "<" -
  523.     ">" {            
  524.         # indent for '<<' and '>>' in multi-line C++ stream manipulation
  525.         if {$nextP == "<<" || $nextP == ">>"} {
  526.         set strm [search -s -f 1 -r 1 "^\[^${nextC}\]+${nextP}" $pbeg]
  527.         set lwhite [posX [pos::math [lindex $strm 1] - 2]]
  528.         }
  529.     }
  530.     }
  531.     # Check if we're in a multi-line '(.....)' if so align to start
  532.     global useFasterButWorseIndentation
  533.     if {!$useFasterButWorseIndentation && ![catch {matchIt ")" $beg 200} paren]} {
  534.     set lwhite [posX [pos::math $paren + 1]]
  535.     }
  536.  
  537.     if {[regexp "^(case\[ \t\].*|\[a-zA-Z\]+):(\[^:\]|\$)" $nextword] && $lwhite > 3 \
  538.       && ![info exists ignore_trailers]} {
  539.     incr lwhite [expr -$indentationAmount/2]
  540.     }
  541.     # get indentation level    
  542.     return [incr lwhite [lindex $ind 1]]
  543. }
  544.  
  545. ## 
  546.  # -------------------------------------------------------------------------
  547.  # 
  548.  # "C++::electricLeft" --
  549.  # 
  550.  #  For those who like to place left braces after 'for', 'else' etc. on 
  551.  #  their own line, this will ensure the left brace is correctly placed.
  552.  # -------------------------------------------------------------------------
  553.  ##
  554. proc C++::electricLeft {} {
  555.     if {[string trim [getText [lineStart [getPos]] [getPos]]] == ""} {
  556.     if {[C_isLineNBI [prevLineStart [getPos]]]} {
  557.         set p [lineStart [getPos]]
  558.         insertText "\{\r"
  559.         goto $p
  560.         shiftLeft
  561.         goto [nextLineStart $p]
  562.         bind::IndentLine
  563.         return
  564.     }
  565.     }
  566.     ::electricLeft
  567. }
  568.  
  569. proc C::electricLeft {} {C++::electricLeft}
  570.  
  571. ## 
  572.  # -------------------------------------------------------------------------
  573.  #     
  574.  # "recurseNoBraceIndent" --
  575.  #    
  576.  # Scans back until we no longer have a 'no brace indent'.  A 'no brace
  577.  # indent' is a 'for', 'if' etc which didn't use '{ ...  }'
  578.  # -------------------------------------------------------------------------
  579.  ##
  580. proc C_recurseNoBraceIndent {pos offset {iselse 0}} {
  581.     set pos [prevLineStart $pos]
  582.     if {[C_isLineNBI $pos]} {
  583.     global indentationAmount
  584.     if {$iselse} {
  585.         set p [text::firstNonWsPos $pos]
  586.         set t [getText $p [pos::math $p + 10]]
  587.         if {[regexp  "(else\[ \t\]+)?if.*" $t]} {
  588.         return [expr {$offset -$indentationAmount}]
  589.         }
  590.     }
  591.     return [C_recurseNoBraceIndent $pos [incr offset [expr -$indentationAmount]] $iselse]
  592.     }
  593.     return $offset
  594.     
  595. }
  596.  
  597. if {${alpha::platform} == "alpha"} {
  598.     set C_recNBI "^\[ \t\]*((\}?\[ \t\]*(if|else\[ \t\]+if)|for)\[ \t\]*\\(.*\\)|\}?\[ \t\]*else)\[ \t\]*(//\[^\r\n\]*)?\[ \t\r\n\]*"
  599. } else {
  600.     set C_recNBI {^[[:blank:]]*((\}?[[:blank:]]*(if|else[[:blank:]]+if)|for)[[:blank:]]*\([^()]*\)|\}?[[:blank:]]*else)[[:blank:]]*(//[^\r\n]*)?\s*}
  601. }
  602.  
  603. ## 
  604.  # -------------------------------------------------------------------------
  605.  #     
  606.  # "isLineNBI" --
  607.  #    
  608.  #  Tests if the given line is a 'no brace indent'.  'pos' is the beginning
  609.  #  of the line in question, else this proc will fail.
  610.  # -------------------------------------------------------------------------
  611.  ##
  612. proc C_isLineNBI {pos} {
  613.     global C_recNBI
  614.     if {![catch {search -s -f 1 -r 1 -l [nextLineStart [nextLineStart $pos]] $C_recNBI $pos} ifelse] } {
  615.     if {[pos::compare $pos == [lindex $ifelse 0]]} {
  616.         if {[pos::compare [lindex $ifelse 1] == [maxPos]] || ([lookAt [lindex $ifelse 1]] != "\{")} {
  617.         return 1 
  618.         }
  619.     }
  620.     }
  621.     return 0
  622. }
  623.  
  624. # use 'catch' to call this proc: error = no, otherwise returns st,end pos
  625. proc C_inCComment {pos} {
  626.     set cS [search -s -f 0 -r 0 -l [pos::math $pos - 1000] "/*" $pos]
  627.     set cE [search -s -f 1 -r 0 -l [pos::math $pos + 1000] "*/" [lindex $cS 1]]
  628.     if {[pos::compare $pos >= [lindex $cE 1]] } {
  629.     error "No"
  630.     } else {
  631.     return [list [lindex $cS 0] [lindex $cE 1]]
  632.     }
  633. }
  634.  
  635. # look for '<<' and '(...)' multi lines.
  636. proc C_isLineMulti {pos} {
  637.     # look for multi-line '(...)'
  638.     if { ![catch {search -s -f 0 -r 1 -l $pos {\).*$} [nextLineStart $pos]} paren] \
  639.       && [pos::compare [nextLineStart $pos] == [pos::math [lindex $paren 1] + 1 ]] } {
  640.     if {[catch {matchIt "\)" [pos::math [lindex $paren 0] - 1] 200} realStart]} {
  641.         return -1
  642.     }
  643.     if {[pos::compare [lineStart $realStart] != [lineStart [lindex $paren 0]]] } {
  644.         set lst [search -s -f 0 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $realStart]
  645.         return [posX [pos::math [lindex $lst 1] - 1]]
  646.     }
  647.     }
  648.     # look for multi-line '<<' or '>>'
  649.     set p $pos
  650.     while {![catch {search -s -f 1 -r 1 -l [nextLineStart $p] "^\[ \t\]*(<<|>>)" $p} strm] } {
  651.     set p [prevLineStart $p]
  652.     }
  653.     if { $p != $pos } {
  654.     set lst [search -s -f 1 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $p]
  655.     return [posX [pos::math [lindex $lst 1] - 1]]
  656.     }
  657.     
  658.     return -1
  659.     
  660. }
  661.  
  662. ## 
  663.  # -------------------------------------------------------------------------
  664.  #   
  665.  # "C_indentCommentLine" --
  666.  #  
  667.  #  Indents a line within a multi-line /* ... */ comment correctly.
  668.  # -------------------------------------------------------------------------
  669.  ##
  670. proc C_indentCommentLine {beg cS cE} {
  671.     # Turn all extraneous leading characters into spaces.
  672.     regsub -all "\[^ \t\r\n\]" [getText [lineStart $cS] $cS] " " lwhite
  673.     set lwhite [text::minSpaceForm $lwhite]
  674.     if {[pos::compare $beg != [lineStart [lindex $cE 0]]] \
  675.       || [text::firstNonWs [pos::math $beg - 1]] == "*" } {
  676.     append lwhite " "    
  677.     }
  678.     
  679.     set text [getText $beg [nextLineStart $beg]]
  680.     regexp "^\[ \t\]*" $text white
  681.     set len [string length $white]
  682.     if {$white != $lwhite} {
  683.     replaceText $beg [pos::math $beg + $len] $lwhite
  684.     }
  685.     goto [pos::math $beg + [expr {[string length $lwhite] +1}]]
  686. }
  687.  
  688.  
  689. ## 
  690.  # -------------------------------------------------------------------------
  691.  #   
  692.  # "C_prevCodeIndent" --
  693.  #  
  694.  #  Find the indent of the previous line
  695.  #  -  If it's the start of the file, return 0 0 (special case)
  696.  #  else
  697.  #  -  if it's a C++ comment, keep looking backwards (so you can offset
  698.  #     C++ comments if you so desire)
  699.  #  -  if it's a C comment, get the indentation of the '/*' not some
  700.  #     intermediate point.
  701.  # -------------------------------------------------------------------------
  702.  ##
  703. proc C_prevCodeIndent {pos} { 
  704.     while {1} {
  705.     if {[pos::compare $pos == [minPos]] \
  706.       || ([catch {search -s -m 0 -f 0 -r 1 -i 0 "^\[ \t\]*\[^ \t\r\n\]" $pos} p]) \
  707.       || $p == "[minPos] [minPos]" } {
  708.         return [list [minPos] [pos::math [minPos] + 1]]
  709.     } else {
  710.         set pp [doubleLookAt [pos::math [lindex $p 1] - 1]]
  711.         if { $pp == "//" } {
  712.         set pos [pos::math [lindex $p 0] - 1]
  713.         } elseif { [string index $pp 0] == "#" } {
  714.         global indentMacros
  715.         if {$indentMacros == "code 0"} {
  716.             break
  717.         }
  718.         set pos [pos::math [lindex $p 0] - 1]
  719.         } elseif { $pp == "*/" } {
  720.         set pos [lindex [search -s -f 0 -r 0 "/*" \
  721.           [pos::math [lindex $p 0] - 1]] 0]
  722.         } elseif { ![catch {set comment [C_inCComment [lindex $p 0]]} ] } {
  723.         set pos [pos::math [lineStart [lindex $comment 0]] - 1]
  724.         #return [text::indentation [lindex $comment 0]] (old style)
  725.         } else {
  726.         break
  727.         }
  728.     }
  729.     }
  730.     return $p
  731. }
  732.  
  733.  
  734. # ◊◊◊◊ Electric routines ◊◊◊◊ #
  735.  
  736. proc C::carriageReturn {} {C++::carriageReturn}
  737. proc C::OptionTitlebar {} {C++::OptionTitlebar}
  738. proc C::OptionTitlebarSelect {item} {C++::OptionTitlebarSelect $item}
  739.  
  740. ## 
  741.  # -------------------------------------------------------------------------
  742.  #     
  743.  # "C++::carriageReturn" --
  744.  #    
  745.  #    Called by the general routine 'carriageReturn'.    We know    no selection 
  746.  #    exists, and we are not inside a block comment.
  747.  # -------------------------------------------------------------------------
  748.  ##
  749. proc C++::carriageReturn {} {
  750.     if {[lookAt [pos::math [getPos] - 1]] == ":"} {
  751.     if {[regexp {[\r\n]} [lookAt [getPos]]]} {
  752.         bind::IndentLine
  753.         endOfLine
  754.         insertText "\r"
  755.     } else {
  756.         set pos [getPos]
  757.         endOfLine
  758.         set t [getText $pos [getPos]]
  759.         replaceText $pos [getPos] ""
  760.         bind::IndentLine
  761.         endOfLine
  762.         insertText "\r"
  763.         insertText $t
  764.     }
  765.     } else {
  766.     insertText "\r"
  767.     }
  768.     catch {bind::IndentLine}
  769. }
  770.  
  771. proc C++::OptionTitlebar {} {
  772.     if {![catch {C++::tryIDEget} ret] && ![regexp {\(No} $ret] } { return $ret }
  773.     # else just scan through, provided the scan will function
  774.     set cid [scancontext create]
  775.     set lines {}
  776.     scanmatch $cid {#.*include.*(<|")(.*)(>|")}  {lappend lines $matchInfo(submatch1)}
  777.     set fid [alphaOpen [win::StripCount [win::Current]] "r"]
  778.     scanfile $cid $fid
  779.     close $fid
  780.     scancontext delete $cid
  781.     return [lsort -ignore $lines]
  782. }
  783.  
  784. proc C++::OptionTitlebarSelect {fname} {
  785.     C++::tryIDEedit $fname
  786. }
  787.  
  788. proc C++::tryIDEget {} {
  789.     global IDE
  790.     switch -- $IDE {
  791.     1 {thinkGetIncludeFiles}
  792.     0 {cw::getIncludeFiles}
  793.     2 {error "No IDE at all!"}
  794.     }
  795. }
  796.  
  797. proc C++::tryIDEedit {fname} {
  798.     global IDE
  799.     switch -- $IDE {
  800.     1 {thinkEditIncludeFile $fname}
  801.     0 {cw::editIncludeFile $fname}
  802.     2 {error "No IDE at all!"}
  803.     }
  804. }
  805.  
  806.